import wx
from zope import interface
import os, sys
from amara import binderytools

APPLICATION_INSTALL_DIR = os.environ.get('SIMPLEWATCHER_INSTALL')
if not APPLICATION_INSTALL_DIR:
    raise EnvironmentError("must set environment variable SIMPLEWATCHER_INSTALL")
sys.path.append('%s/lib' % APPLICATION_INSTALL_DIR)

from watch_builder.gui.watchobjects import base
from watch_builder.gui.evthandle import registrar
from watch_builder.builder import buildKlass
from watch_builder.gui import utils, dialogs
from watch_builder.gui.watchobjects import interfaces as wa_ints


class DataObjectHelper(object):
    """helper object for the window helper... updates child objects with new data"""
    def __init__(self, parent):
        self.parent = parent
        
    def watchWidgetUpdate(self, child, **kwargs):
        child.finalAction = kwargs.get("final", False)
        child.restBetweenIter = kwargs.get("rest", '')
        child.startOnInit = kwargs.get("startOnInit", False)
    
    def branchWidgetUpdate(self, child, **kwargs):
        pass
    
    def rootWidgetUpdate(self, child, **kwargs):
        pass
    
    def executePathWidgetUpdate(self, child, **kwargs):
        pass


class MenuBuilder(object):
    """simple menu builder to help out the watch object window helper"""
    def __init__(self, parent):
        self.parent = parent
    
    def rootContextMenu(self, event):
        return self.parent.window.ContextMenu(event, wa_ints.IRootDataObject)

    def watchContextMenu(self, event):
        return self.parent.window.ContextMenu(event, wa_ints.IWatchDataObject)

    def branchContextMenu(self, event):
        return self.parent.window.ContextMenu(event, wa_ints.IBranchDataObject)

    def executePathContextMenu(self, event):
        return self.parent.window.ContextMenu(event, wa_ints.IExecutePathDataObject)
 
    def rootContextEdit(self, event):
        return self.parent.window.ContextEdit(event, wa_ints.IRootDataObject, title="Edit Root")

    def watchContextEdit(self, event):
        return self.parent.window.ContextEdit(event, wa_ints.IWatchDataObject, title="Edit Watch")

    def branchContextEdit(self, event):
        return self.parent.window.ContextEdit(event, wa_ints.IBranchDataObject, title="Edit Branch")

    def executePathContextEdit(self, event):
        return self.parent.window.ContextEdit(event, wa_ints.IExecutePathDataObject, title="Edit Execute Path")



class WatchObjectWindowHelper(object):
    """helper class for watch object window"""
    
    def __init__(self, window):
        self.window = window
        self.do_helper = DataObjectHelper(self)
        self.menu_builder = MenuBuilder(self)
    
    def findBestCoord(self, loc, child):
        """find best coord really does more than it seems like
           i'd like to move the other_child._children.append out
           of this method, but it's the only place where we will
           have access to this data, esp since findBest produces
           a point either to the right or down from the drop point
        """
        best_dist = sys.maxint
        best_point = loc
        best_child = None
        for c in self.window._children.keys():
            other_child, bmp = self.window._children[c]
            valid, message = other_child.schema.validate_child(child, checkOnly=True)
            if valid:
                # c is the placement point of the child, not the center.  we want to measure from the center of each child.
                newx, newy = (c[0] + 20, c[1] + 20)
                right_or_down = self.chooseRightOrDown(loc, (newx, newy))
                if right_or_down is None:
                    return (None, None)
                # reset right_or_down back to the topleft corner
                right_or_down = (right_or_down[0] - 20, right_or_down[1] - 20)
                dist = utils.calculateDistance(loc, right_or_down)
                if dist < best_dist:
                    best_dist = dist
                    best_point = right_or_down
                    best_child = other_child
        
        if best_dist == sys.maxint:
            return (None, None)
        best_child._children.append(child)
        child._parent = best_child
        return best_point

    
    def chooseRightOrDown(self, loc, attachLoc):
        attach_right = (attachLoc[0] + 40, attachLoc[1])
        attach_down = (attachLoc[0], attachLoc[1] + 40)
        attach_angle = (attachLoc[0] + 40, attachLoc[1] + 40)
        rdist = utils.calculateDistance(loc, attach_right)
        ddist = utils.calculateDistance(loc, attach_down)
        adist = utils.calculateDistance(loc, attach_angle)
        ar_used = False
        ad_used = False
        aa_used = False
        if attach_down in self.window._children:
            ad_used = True
        if attach_right in self.window._children:
            ar_used = True
        if attach_angle in self.window._children:
            aa_used = True
        if not ar_used and not ad_used and not aa_used:
            if rdist < ddist and rdist < adist:
                return attach_right
            elif adist < rdist and adist < ddist:
                return attach_angle
            elif ddist < adist and ddist < rdist:
                return attach_down
        elif not ar_used and not ad_used and aa_used:
            if rdist < ddist:
                return attach_right
            else:
                return attach_down
        elif not ar_used and not aa_used and ad_used:
            if adist < rdist:
                return attach_angle
            else:
                return attach_right
        elif not ad_used and not aa_used and ar_used:
            if adist < ddist:
                return attach_angle
            else:
                return attach_down
        elif aa_used and ar_used and not ad_used:
            return attach_down
        elif aa_used and ad_used and not ar_used:
            return attach_right
        elif ar_used and ad_used and not aa_used:
            return attach_angle
        else:
            # all are used, return None
            return None

        
    def findContextMenuFuncFor(self, child):
        interfaces_and_functions = [
            (wa_ints.IWatchDataObject, self.menu_builder.watchContextMenu), (wa_ints.IRootDataObject, self.menu_builder.rootContextMenu),
            (wa_ints.IBranchDataObject, self.menu_builder.branchContextMenu), (wa_ints.IExecutePathDataObject, self.menu_builder.executePathContextMenu),
        ]
        for int, func in interfaces_and_functions:
            if int.providedBy(child):
                return func
        return None
    
    
    def findContextEditFuncFor(self, child):
        interfaces_and_functions = [
            (wa_ints.IWatchDataObject, self.menu_builder.watchContextEdit), (wa_ints.IRootDataObject, self.menu_builder.rootContextEdit),
            (wa_ints.IBranchDataObject, self.menu_builder.branchContextEdit), (wa_ints.IExecutePathDataObject, self.menu_builder.executePathContextEdit),
        ]
        for int, func in interfaces_and_functions:
            if int.providedBy(child):
                return func
        return None


    def findUpdateFuncFor(self, child):
        interfaces_and_functions = [
            (wa_ints.IWatchDataObject, self.do_helper.watchWidgetUpdate), (wa_ints.IRootDataObject, self.do_helper.rootWidgetUpdate),
            (wa_ints.IBranchDataObject, self.do_helper.branchWidgetUpdate), (wa_ints.IExecutePathDataObject, self.do_helper.executePathWidgetUpdate),
        ]
        for int, func in interfaces_and_functions:
            if int.providedBy(child):
                return func
        return None
    

    def findNearestChild(self, loc, inttype):
        best_dist = sys.maxint
        best_child = None
        for c in self.window._children.keys():
            child, bmp = self.window._children[c]
            if not inttype.providedBy(child):
                continue
            # c is the placement point of the child, not the center.  we want to measure from the center of each child.
            newx, newy = (c[0] + 20, c[1] + 20)
            dist = utils.calculateDistance(loc, (newx, newy))
            if dist < best_dist:
                best_dist = dist
                best_child = child
        return best_child
